package com.g001.model;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.state.StateBasedGame;

import com.g001.enums.BlockType;
import com.g001.enums.MovingBlockType;
import com.g001.enums.ProxBlockType;

public abstract class IBlock {
	
	public float x;
	public float y;
	
	public int i;
	public int j;
	
	protected UUID id;
	
	// initial place to set the block on screen
	protected Vector2f gamePosition;
	
	// shape for action
	protected Shape shape;
	
	// the proxy blocks of this block
	protected Map<ProxBlockType, IBlock> proxiBlocks;
	
	// the kind of block
	protected BlockType blockType;
	
	// direction of the block movement
	protected MovingBlockType movingBlockType;
	
	// remove 
	protected boolean remove;
	 
	// destroy
	protected boolean destroy;
	
	// after that the update proxy is done, the var
	protected boolean matchesChecked;
	
	// check if the block is falling
	public abstract boolean isFalling();
	
	protected void updateShape(){
		 shape.setX(x+BlockType.TRANSPARENT_BODER_X);
		 shape.setY(y+BlockType.TRANSPARENT_BODER_Y);
	}
	
	public abstract void update(GameContainer container, StateBasedGame game, int delta)
			throws SlickException;
	
	public abstract void render(GameContainer container, StateBasedGame game, Graphics g)
			throws SlickException;
	
	public boolean isMouseOnIt(int mouseX, int mouseY) {
		return shape.contains(mouseX, mouseY);
	}
	
	///////////////////////////
	// Proxy Blocks
	///////////////////////////
	public IBlock getProxyBlock(final ProxBlockType key){
		return proxiBlocks.get(key);
	}
	
	public void setProxyBlock(final ProxBlockType key, IBlock block){
		proxiBlocks.put(key, block);
		if(block!=null){
			block.proxiBlocks.put(ProxBlockType.getOppositeBlockType(key), this);
		}
	}
	
	
	protected void removeReference(){
		IBlock b;
		for(ProxBlockType key : proxiBlocks.keySet()) {
			b = proxiBlocks.get(key);
			if(b!=null)
			switch(key){
				case N :  if(b.getProxyBlock(ProxBlockType.S) == this) b.setProxyBlock(ProxBlockType.S, null) ;break;
				case S :  if(b.getProxyBlock(ProxBlockType.N) == this) b.setProxyBlock(ProxBlockType.N, null) ;break;
				case NE : if(b.getProxyBlock(ProxBlockType.SW) == this) b.setProxyBlock(ProxBlockType.SW, null) ;break;
				case NW : if(b.getProxyBlock(ProxBlockType.SE) == this) b.setProxyBlock(ProxBlockType.SE, null) ;break;
				case SE : if(b.getProxyBlock(ProxBlockType.NW) == this) b.setProxyBlock(ProxBlockType.NW, null) ;break;
				case SW : if(b.getProxyBlock(ProxBlockType.NE) == this) b.setProxyBlock(ProxBlockType.NE, null) ;break;
			}
		}
	}
	
	public void clearProxyBlock(final ProxBlockType key){
		proxiBlocks.put(key, null);
	}
	
	public void clearAllProxyBlocks(){
		removeReference();
		for(ProxBlockType key : ProxBlockType.values())
			proxiBlocks.put(key, null);
	}
	
	// return true is this and b are contiguous blocks
	public boolean isContigousBlock(IBlock block){
		if(block != null) {
			for(ProxBlockType key : ProxBlockType.values())
				if(proxiBlocks.get(key)==block){
					return true;
				}
			}
		return false;
	}
	
	// return the last identical block in the direction ProxBlockType among the proxy blocks
	public IBlock findIdenticalBlock(ProxBlockType key, IBlock b){
		if(b == null)  return  this;
		else if(this.getBlockType().getId() == b.getBlockType().getId()){
			return b.findIdenticalBlock(key, b.getProxyBlock(key));
		} else {
			return this;
		}
	}

	
	// getter & setter

	public BlockType getBlockType() {
		return blockType;
	}

	public void setBlockType(BlockType blockType) {
		this.blockType = blockType;
	}
	
	
	protected abstract void initPosition();

	public abstract void setSelected(boolean b);
	public abstract boolean isSelected();
	
	public abstract String toString();
	public abstract void setShock();
	
	
	public void printSituation(){
		System.out.println();System.out.println();System.out.println();
		 System.out.println(espace(String.valueOf(proxiBlocks.get(ProxBlockType.NW)).length())+"   "+proxiBlocks.get(ProxBlockType.N));
		 System.out.println(proxiBlocks.get(ProxBlockType.NW) +" | "+espace(String.valueOf(proxiBlocks.get(ProxBlockType.N)).length())+" | "+ proxiBlocks.get(ProxBlockType.NE));
		 System.out.println(proxiBlocks.get(ProxBlockType.SW) +" | "+i+espace(String.valueOf(proxiBlocks.get(ProxBlockType.N)).length()-2)+","+j+" | "+ proxiBlocks.get(ProxBlockType.SE));
		 System.out.println(espace(String.valueOf(proxiBlocks.get(ProxBlockType.SW)).length())+"   "+proxiBlocks.get(ProxBlockType.S));
	}

	private String espace(int i){
		StringBuffer s = new StringBuffer();
		for(int j = 0; j<i; j++)
			s.append(" ");
		return s.toString();
	}
	
	public int updateProxy(){
		int result = 0;
		IBlock S = getProxyBlock(ProxBlockType.S);
		if(S != null){			
			IBlock S_NW = S.getProxyBlock(ProxBlockType.NW);
			IBlock S_NE = S.getProxyBlock(ProxBlockType.NE);
			setProxyBlock(ProxBlockType.SW, S_NW);
			setProxyBlock(ProxBlockType.SE, S_NE);
			if(S_NW != null){
				IBlock S_NW_N = S_NW.getProxyBlock(ProxBlockType.N);
				if(S_NW_N instanceof BorderBlock) {
					setProxyBlock(ProxBlockType.NW, null);
				} else {
					setProxyBlock(ProxBlockType.NW, S_NW_N);
				}
			}
			if(S_NE != null){
				IBlock S_NE_N = S_NE.getProxyBlock(ProxBlockType.N);
				if(S_NE_N instanceof BorderBlock) {
					setProxyBlock(ProxBlockType.NE, null);
				} else {
					setProxyBlock(ProxBlockType.NE, S_NE_N);
				}
			}
		//	System.out.println("updateProxy("+this+") udpated !!!!");
			setProxyUpdated(true);
			matchesChecked = true;
		}
		return result;
	}
	
	public abstract void moveTo(final IBlock b);
	public abstract void moveBack(final IBlock b);
	public abstract boolean isSwitching();
	public abstract void setSwitching(boolean switching);
	public abstract boolean isProxyUpdated();
	public abstract void setProxyUpdated(boolean proxyUpdated);
	public abstract void setRemove(boolean b);
	public abstract boolean isBusy();
	
	public boolean isRemove() {
		return remove;
	}
	
	public boolean processBlock2remove(ProxBlockType proxyBlockDest){
		IBlock b = this;
		List<IBlock> blocks2remove = new ArrayList<IBlock>();
			while(true){
				if(b!=null && b.getBlockType().getId() == getBlockType().getId()){
					blocks2remove.add(b);
					b = b.getProxyBlock(proxyBlockDest);
				} else {
					break;
				}
			} // while			
			if(blocks2remove.size()>=3){
				for(IBlock bc : blocks2remove){
					bc.setRemove(true);
				}
			}
		return blocks2remove.size()>=3;
	}

	public boolean isDestroy() {
		return destroy;
	}

	public boolean isMatchesChecked() {
		return matchesChecked;
	}

	public Map<ProxBlockType, IBlock> getProxiBlocks() {
		return proxiBlocks;
	}

	public void setProxiBlocks(Map<ProxBlockType, IBlock> proxiBlocks) {
		this.proxiBlocks = proxiBlocks;
	}
	
	public UUID getId(){
		return id;
	}
	
	public void setId(UUID uuid){
		id = new UUID(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
	}



}
